package com.github.laozhaishaozuo.asm;

import java.io.FileOutputStream;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

/**
 * @author zhangshimin
 * @date 2018/12/03
 */
public class GenerateBean extends ClassLoader implements Opcodes {

	private static final String CLASS_NAME = "TestBean";
	private static final String PROPERTY_NAME_F = "f";
	private static final String PROPERTY_NAME_F_DESC = "I";

	public static void main(String[] args) throws Exception {
		// 定义一个叫做TestBean的类
		ClassWriter cw = new ClassWriter(0);
		cw.visit(V1_1, ACC_PUBLIC, CLASS_NAME, null, "java/lang/Object", null);
		generateDefaultConstruct(cw);
		// 构造属性
		cw.visitField(ACC_PRIVATE, PROPERTY_NAME_F, PROPERTY_NAME_F_DESC, null, null).visitEnd();
		// 生成getter,setter,方法
		createSetter(cw, PROPERTY_NAME_F, PROPERTY_NAME_F_DESC, Integer.class);
		createGetter(cw, PROPERTY_NAME_F, PROPERTY_NAME_F_DESC, Integer.class);

		MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "checkAndSetF", "(I)V", null, null);
		mv.visitCode();
		Label l0 = new Label();
		Label l1 = new Label();
		Label l2 = new Label();
		Label l3 = new Label();
		Label l4 = new Label();

		mv.visitLabel(l0);
		mv.visitVarInsn(ILOAD, 1);
		mv.visitJumpInsn(IFLE, l3);

		mv.visitLabel(l1);
		mv.visitVarInsn(ALOAD, 0);
		mv.visitVarInsn(ILOAD, 1);
		mv.visitFieldInsn(PUTFIELD, CLASS_NAME, PROPERTY_NAME_F, "I");

		mv.visitLabel(l2);
		mv.visitJumpInsn(GOTO, l4);

		mv.visitLabel(l3);
		mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
		mv.visitInsn(DUP);
		mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "()V", false);
		mv.visitInsn(ATHROW);

		mv.visitLabel(l4);
		mv.visitInsn(RETURN);

		mv.visitMaxs(2, 2);
		mv.visitEnd();


		// 获取生成的class文件对应的二进制流
		byte[] code = cw.toByteArray();
		// 将二进制流写到本地磁盘上
		FileOutputStream fos = new FileOutputStream(CLASS_NAME + ".class");
		fos.write(code);
		fos.close();


	}

	public static void generateDefaultConstruct(ClassWriter cw) {
		// 生成默认的构造方法
		MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);

		// 生成构造方法的字节码指令
		mv.visitVarInsn(ALOAD, 0);
		mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
		mv.visitInsn(RETURN);
		mv.visitMaxs(1, 1);
		mv.visitEnd();

	}

	public static void createSetter(ClassWriter cw, String propertyName, String type, Class<?> setterClass) {
		String methodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
		MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, methodName, "(" + type + ")V", null, null);
		mv.visitVarInsn(ALOAD, 0);
		mv.visitVarInsn(Type.getType(setterClass).getOpcode(ILOAD), 1);
		mv.visitFieldInsn(PUTFIELD, CLASS_NAME, propertyName, type);
		mv.visitInsn(RETURN);
		mv.visitMaxs(0, 0);
	}

	public static void createGetter(ClassWriter cw, String propertyName, String returnType, Class<?> returnClass) {
		String methodName = "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
		MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, methodName, "()" + returnType, null, null);
		mv.visitVarInsn(ALOAD, 0);
		mv.visitFieldInsn(GETFIELD, CLASS_NAME, propertyName, returnType);
		mv.visitInsn(Type.getType(returnClass).getOpcode(IRETURN));
		mv.visitMaxs(0, 0);
	}
}
